package org.noworks.android.mvc;

import java.util.HashMap;
import java.util.Map;

import org.noworks.android.mvc.activities.MvcSherlockFragmentActivity;
import org.noworks.android.mvc.annotations.Controller;
import org.noworks.android.mvc.exceptions.ActionNotFoundException;
import org.noworks.android.mvc.exceptions.ControllerNotFoundException;

import android.app.Application;
import android.content.Intent;

/**
 * Override of the stock Android Application class to provide helpers for
 * routing to proper ActionMethods
 * 
 * @author Mike Nowak
 * 
 */
public abstract class MvcApplication extends Application {

	public static final String ACTION = "action"; /*
												 * Static string representing
												 * the Action constant
												 */

	private Map<String, MvcSherlockFragmentActivity> controllers; // Last
																	// performed
	private String lastAction; // Last performed action

	private Map<String, String> controllerIntents;

	/**
	 * Call and ActionMethod with no arguments for a Controller
	 * 
	 * @param controllerName
	 *            Controller to call the ActionMethod in
	 * @param action
	 *            ActionMethod to call
	 * @throws ActionNotFoundException
	 *             Thrown when there is no ActionMethod for the given action
	 * @throws ControllerNotFoundException
	 *             Thrown when there is no controller found for the given
	 *             controllerName
	 * @throws IllegalArgumentException
	 *             Thrown when their is no zero parameter method for the action
	 *             to be performed
	 */
	public void action(String controllerName, String action)
			throws ActionNotFoundException, ControllerNotFoundException,
			IllegalArgumentException {
		MvcSherlockFragmentActivity controller = controllers
				.get(controllerName);
		if (controller == null) {
			String intentStr = controllerIntents.get(controllerName);
			if (intentStr != null) {
				Intent intent = new Intent(intentStr);
				intent.putExtra(ACTION, action);
				startActivity(intent);
				return;
			}
		}
		if (controller == null)
			throw new ControllerNotFoundException(
					"No controller found matching name: " + controllerName);
		controller.action(action);
	}

	/**
	 * Call the ActionMethod with arguments for a Controller
	 * 
	 * @param controllerName
	 *            Controller to call the ActionMethod in
	 * @param action
	 *            ActionMethod to call
	 * @param args
	 *            Arguments to pass to the ActionMethod
	 * @throws ActionNotFoundException
	 *             Thrown when their is no ActionMethod for the given action
	 * @throws ControllerNotFoundException
	 *             Thrown when there is no controller found for the given
	 *             controllerName
	 * @throws IllegalArgumentException
	 *             Thrown when their is method with the given parameters for
	 *             method
	 */
	public void actionArguments(String controllerName, String action,
			Object... args) throws ActionNotFoundException,
			ControllerNotFoundException, IllegalArgumentException {
		MvcSherlockFragmentActivity controller = controllers
				.get(controllerName);
		if (controller == null) {
			String intentStr = controllerIntents.get(controllerName);
			if (intentStr != null) {
				throw new ControllerNotFoundException(
						"Cannot instantiate controllers with arguments inside routing context");
			}
		}
		if (controller == null && !controllerName.equals(getControllerName()))
			throw new ControllerNotFoundException(
					"No controller found matching name: " + controllerName);
		else
			controller.actionArguments(action, args);
	}

	/**
	 * Convenience method should you ever call a method inside your application
	 * 
	 * @return String representing the applications controller name
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public final String getControllerName() {
		Class clazz = getClass();
		String controllerName = clazz.getName();
		if (clazz.isAnnotationPresent(Controller.class)) {
			Controller ca = (Controller) clazz.getAnnotation(Controller.class);
			if (!ca.value().equals(""))
				controllerName = ca.value();
		}
		return controllerName;
	}

	/**
	 * Gets the last action performed by the application
	 * 
	 * @return Last performed action
	 */
	public String getLastAction() {
		return lastAction;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		controllers = new HashMap<String, MvcSherlockFragmentActivity>();
		controllerIntents = new HashMap<String, String>();
	}

	public void addFutureController(String controller, String intent) {
		controllerIntents.put(controller, intent);
	}

	/**
	 * Register a Controller to be used when routing actions
	 * 
	 * @param name
	 *            Name to use map the Controller to
	 * @param controller
	 *            The Controller to map
	 */
	public void registerController(String name,
			MvcSherlockFragmentActivity controller) {
		controllers.put(name, controller);
	}

	/**
	 * Set the last action performed by the application
	 * 
	 * @param action
	 *            Last performed action
	 */
	public void setLastAction(String action) {
		lastAction = action;
	}
}
